home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
...taking it to the Macs!
/
...taking it to the Macs!.iso
/
Extras
/
ActiveX Mac SDK
/
ActiveX SDK
/
Container Common
/
binhex.c
< prev
next >
Wrap
Text File
|
1997-01-03
|
36KB
|
1,393 lines
#ifdef _MSC_VER
#include <macos\msvcmac.h>
#include <string.h>
#endif
//----------------------------------------------
// Global includes
//----------------------------------------------
#include <Types.h>
#include <Files.h>
#include "BinHex.h"
//----------------------------------------------
// Local includes
//----------------------------------------------
#include <Resources.h>
#include <Finder.h>
#include <Errors.h>
#include <TextUtils.h>
#include <PLStringFuncs.h>
//----------------------------------------------
// InputBuffer structure
//----------------------------------------------
#define cIgnoreChecksums 0
#define cBuffer_Size 16384
typedef struct {
UInt32 FileOffset;
UInt8 Buffer[cBuffer_Size];
UInt32 BytesInBuffer;
UInt32 BufferOffset;
UInt32 BytesAvailable;
} tInputBuffer;
typedef struct {
UInt32 FileOffset;
UInt8 Buffer[cBuffer_Size];
UInt32 BytesInBuffer;
UInt32 BufferOffset;
UInt32 BytesAvailable;
Boolean TerminatorFound;
UInt8 ConversionTable[256];
int HoldingPhase;
UInt8 HoldingByte;
UInt8 PreviousByte;
Boolean RepeatFlagFound;
} tBinHexBuffer;
#pragma options align=mac68k
typedef struct {
Str63 FileName;
OSType FileType;
OSType FileCreator;
UInt16 FinderFlags;
UInt32 DataLength;
UInt32 ResourceLength;
} tBinHexFileHeader;
#pragma options align=reset
//----------------------------------------------
// tBinHexParser
//----------------------------------------------
typedef enum {
cValidating_ParserState = 0,
cHeaderNameLength_ParserState,
cHeaderName_ParserState,
cHeaderNamePad_ParserState,
cHeader_ParserState,
cHeaderChecksum_ParserState,
cDataFork_ParserState,
cDataForkChecksum_ParserState,
cResourceFork_ParserState,
cResourceForkChecksum_ParserState,
cPassThrough_ParserState,
cDiscard_ParserState
} tBinHexParserState;
struct tBinHexParser
{
tBinHexClientData * fClientData;
tBinHexParserCallbacks * fCallbacks;
tInputBuffer * fInputBuffer;
tBinHexBuffer * fBinHexBuffer;
tBinHexParserState fParserState;
tBinHexFileHeader fHeader;
UInt16 fCalculatedChecksum;
UInt16 fActualChecksum;
long fByteCount; // Meaning is state-dependent:
// cValidating_ParserState: # bytes of header accumulated
// cDataFork_ParserState: # bytes of data fork remaining
// cDataGap_ParserState: # bytes of data gap remaining
// cResourceFork_ParserState: # bytes of resource fork remaining
// cResourceGap_ParserState: # bytes of resource gap remaining
// cPassThrough_ParserState: no meaning
// cDiscard_ParserState: no meaning
};
//----------------------------------------------
// Local prototypes
//----------------------------------------------
static OSErr SkipReturn(tInputBuffer *pBuffer);
static OSErr ReadFileToReturn(tInputBuffer *pBuffer, Boolean pKeepSpaces, void *pData, long pMaxLength, long *rActualLength);
static OSErr ReadOneCharacter(tInputBuffer *pBuffer, char *rCharacter);
static OSErr FindBinHexHeader(tInputBuffer *pBuffer);
static Boolean BinHexBuffer_New(tBinHexBuffer **rBuffer);
static void BinHexBuffer_Dispose(tBinHexBuffer *pBuffer);
static OSErr BinHexBuffer_LoadFromFile(tBinHexBuffer *pBuffer, tInputBuffer *pInputBuffer, short pFileRef);
static Boolean BinHexBuffer_Stuff(tBinHexBuffer *pBuffer, UInt8 *pData, UInt32 pNumberOfBytes, UInt32 *rBytesStuffed);
static OSErr BinHexBuffer_ReadByte(tBinHexBuffer *pBuffer, UInt8 *rByte, UInt16 *rChecksum);
static OSErr BinHexBuffer_ReadBytes(tBinHexBuffer *pBuffer, UInt8 *pReadBuffer, UInt32 pNumberOfBytes, UInt16 *rChecksum);
static void AddByteToBinHexChecksum(UInt8 pValue, UInt16 *rChecksum);
static void AddBytesToBinHexChecksum(UInt8 *pValues, UInt32 pNumberOfBytes, UInt16 *rChecksum);
static OSErr GetBinHexFileHeader(tBinHexBuffer *pBuffer, Boolean *rHeaderIsValid, tBinHexFileHeader *rHeader);
static Boolean CopyBinHexToFile(tBinHexBuffer *pBuffer, tInputBuffer *pInputBuffer, short pSourceFileRef, short pDestFileRef, UInt32 pNumberOfBytes, UInt16 *rChecksum);
// InputBuffer
static Boolean InputBuffer_New(tInputBuffer **rBuffer);
static void InputBuffer_Dispose(tInputBuffer *pBuffer);
static OSErr InputBuffer_LoadFromFile(tInputBuffer *pBuffer, short pFileRef);
static long InputBuffer_Stuff(tInputBuffer *pInputBuffer, void *pData, long pNumberOfBytes);
static void InputBuffer_DeleteOldData(tInputBuffer *pInputBuffer);
static void InputBuffer_GetPointer(tInputBuffer *pInputBuffer, void **rData, long *rMaxNumberOfBytes);
static void InputBuffer_BytesAdded(tInputBuffer *pInputBuffer, long pNumberOfBytes);
// BinHexParser
static Boolean BinHexParser_WriteDecodedData(tBinHexParser *pParser, void *pData, long pDataLength);
//----------------------------------------------
// Global functions
//----------------------------------------------
Boolean UnpackBinHexFile(FSSpec *pSourceFile, FSSpec *pDestFile, FSSpec *rDestFile)
{
OSErr vErr;
short vSourceFileRef;
tInputBuffer * vInputBuffer;
tBinHexBuffer * vBinHexBuffer;
FSSpec vDestFile;
short vDestFileRef;
tBinHexFileHeader vHeader;
UInt16 vCalculatedChecksum;
UInt16 vFileChecksum;
FInfo vFinderInfo;
Boolean vHeaderIsValid;
// Open the source file
vErr = FSpOpenDF(pSourceFile, fsRdPerm, &vSourceFileRef);
if (vErr != noErr) return false;
if (!InputBuffer_New(&vInputBuffer))
goto errorExit;
if (!BinHexBuffer_New(&vBinHexBuffer))
goto errorExit2;
// Search for the BinHex header
vErr = InputBuffer_LoadFromFile(vInputBuffer, vSourceFileRef);
if (vErr != noErr) goto errorExit3;
vErr = FindBinHexHeader(vInputBuffer);
if (vErr != noErr) goto errorExit3;
// Read the header
vErr = BinHexBuffer_LoadFromFile(vBinHexBuffer, vInputBuffer, vSourceFileRef);
if (vErr != noErr) goto errorExit3;
vErr = GetBinHexFileHeader(vBinHexBuffer, &vHeaderIsValid, &vHeader);
if (vErr != noErr) goto errorExit3;
if (!vHeaderIsValid) goto errorExit3;
// Create the destination FSSpec
if (pDestFile != nil)
vDestFile = *pDestFile;
else {
vDestFile = *pSourceFile;
PLstrcpy(vDestFile.name, vHeader.FileName);
}
// Create the destination file
FSpDelete(&vDestFile);
vErr = FSpCreate(&vDestFile, vHeader.FileCreator, vHeader.FileType, 0);
if (vErr != noErr) goto errorExit3;
// Set the finder flags
vErr = FSpGetFInfo(&vDestFile, &vFinderInfo);
if (vErr != noErr) goto errorExit4;
vFinderInfo.fdFlags = vHeader.FinderFlags & ~(kIsInvisible | kHasBeenInited | kIsOnDesk);
vErr = FSpSetFInfo(&vDestFile, &vFinderInfo);
if (vErr != noErr) goto errorExit4;
// Open the data fork
vErr = FSpOpenDF(&vDestFile, fsRdWrPerm, &vDestFileRef);
if (vErr != noErr) goto errorExit4;
// Copy the data fork
vCalculatedChecksum = 0;
if (!CopyBinHexToFile(vBinHexBuffer, vInputBuffer, vSourceFileRef, vDestFileRef, vHeader.DataLength, &vCalculatedChecksum))
goto errorExit5;
AddByteToBinHexChecksum(0, &vCalculatedChecksum);
AddByteToBinHexChecksum(0, &vCalculatedChecksum);
if (vBinHexBuffer->BytesAvailable < sizeof(UInt16)) {
vErr = BinHexBuffer_LoadFromFile(vBinHexBuffer, vInputBuffer, vSourceFileRef);
if (vErr != noErr) goto errorExit5;
}
vErr = BinHexBuffer_ReadBytes(vBinHexBuffer, (UInt8 *)&vFileChecksum, sizeof(UInt16), nil);
if (vErr != noErr) goto errorExit5;
#if !cIgnoreChecksums
if (vCalculatedChecksum != vFileChecksum)
goto errorExit5;
#endif
// Close the data fork
FSClose(vDestFileRef);
// Open the resource fork
vErr = FSpOpenRF(&vDestFile, fsRdWrPerm, &vDestFileRef);
if (vErr != noErr) goto errorExit4;
// Copy the resource fork
vCalculatedChecksum = 0;
if (!CopyBinHexToFile(vBinHexBuffer, vInputBuffer, vSourceFileRef, vDestFileRef, vHeader.ResourceLength, &vCalculatedChecksum))
goto errorExit5;
AddByteToBinHexChecksum(0, &vCalculatedChecksum);
AddByteToBinHexChecksum(0, &vCalculatedChecksum);
if (vBinHexBuffer->BytesAvailable < sizeof(UInt16)) {
vErr = BinHexBuffer_LoadFromFile(vBinHexBuffer, vInputBuffer, vSourceFileRef);
if (vErr != noErr) goto errorExit5;
}
vErr = BinHexBuffer_ReadBytes(vBinHexBuffer, (UInt8 *)&vFileChecksum, sizeof(UInt16), nil);
if (vErr != noErr) goto errorExit5;
#if !cIgnoreChecksums
if (vCalculatedChecksum != vFileChecksum)
goto errorExit5;
#endif
// Close the resource fork
FSClose(vDestFileRef);
// Close the source file
BinHexBuffer_Dispose(vBinHexBuffer);
InputBuffer_Dispose(vInputBuffer);
FSClose(vSourceFileRef);
if (rDestFile != nil)
*rDestFile = vDestFile;
return true;
errorExit5:
FSClose(vDestFileRef);
errorExit4:
FSpDelete(&vDestFile);
errorExit3:
BinHexBuffer_Dispose(vBinHexBuffer);
errorExit2:
InputBuffer_Dispose(vInputBuffer);
errorExit:
FSClose(vSourceFileRef);
return false;
}
Boolean IsBinHexFile(FSSpec *pSourceFile, FSSpec *rDestFile)
{
OSErr vErr;
short vSourceFileRef;
tInputBuffer * vInputBuffer;
tBinHexBuffer * vBinHexBuffer;
FSSpec vDestFile;
tBinHexFileHeader vHeader;
Boolean vHeaderIsValid;
// Open the source file
vErr = FSpOpenDF(pSourceFile, fsRdPerm, &vSourceFileRef);
if (vErr != noErr) return false;
if (!InputBuffer_New(&vInputBuffer))
goto errorExit;
if (!BinHexBuffer_New(&vBinHexBuffer))
goto errorExit2;
// Search for the BinHex header
vErr = InputBuffer_LoadFromFile(vInputBuffer, vSourceFileRef);
if (vErr != noErr) goto errorExit3;
vErr = FindBinHexHeader(vInputBuffer);
if (vErr != noErr) goto errorExit3;
// Read the header
vErr = BinHexBuffer_LoadFromFile(vBinHexBuffer, vInputBuffer, vSourceFileRef);
if (vErr != noErr) goto errorExit3;
vErr = GetBinHexFileHeader(vBinHexBuffer, &vHeaderIsValid, &vHeader);
if (vErr != noErr) goto errorExit3;
if (!vHeaderIsValid) goto errorExit3;
// Create the destination FSSpec
vDestFile = *pSourceFile;
PLstrcpy(vDestFile.name, vHeader.FileName);
// Close the source file
BinHexBuffer_Dispose(vBinHexBuffer);
InputBuffer_Dispose(vInputBuffer);
FSClose(vSourceFileRef);
// Return
if (rDestFile != nil)
*rDestFile = vDestFile;
return true;
errorExit3:
BinHexBuffer_Dispose(vBinHexBuffer);
errorExit2:
InputBuffer_Dispose(vInputBuffer);
errorExit:
FSClose(vSourceFileRef);
return false;
}
//----------------------------------------------
// Local functions
//----------------------------------------------
static OSErr SkipReturn(tInputBuffer *pBuffer)
{
OSErr vErr;
vErr = noErr;
do {
UInt32 vIndex;
UInt8 * vBufferData;
if (pBuffer->BytesAvailable == 0)
vErr = eofErr;
vBufferData = &pBuffer->Buffer[pBuffer->BufferOffset];
for (vIndex = 0; vIndex < pBuffer->BytesAvailable; vIndex++) {
switch (*vBufferData) {
case 0x09:
case 0x0d:
case 0x0a:
case ' ':
break;
default:
pBuffer->BufferOffset += vIndex;
pBuffer->BytesAvailable -= vIndex;
return noErr;
} // switch
vBufferData++;
} // for
pBuffer->BufferOffset = pBuffer->BytesInBuffer;
pBuffer->BytesAvailable = 0;
} while (vErr == noErr);
return vErr;
}
static OSErr ReadFileToReturn(tInputBuffer *pBuffer, Boolean pKeepSpaces, void *pData, long pMaxLength, long *rActualLength)
{
OSErr vErr;
UInt8 * vReadData;
long vActualLength;
vErr = noErr;
vReadData = (UInt8 *)pData;
vActualLength = 0;
do {
UInt32 vIndex;
UInt8 * vBufferData;
if (pBuffer->BytesAvailable == 0)
vErr = eofErr;
vBufferData = &pBuffer->Buffer[pBuffer->BufferOffset];
for (vIndex = 0; vIndex < pBuffer->BytesAvailable; vIndex++) {
switch (*vBufferData) {
case 0x09:
case 0x0d:
case 0x0a:
case ' ':
if ((*vBufferData != ' ') || (!pKeepSpaces)) {
pBuffer->BufferOffset += vIndex + 1;
pBuffer->BytesAvailable -= vIndex + 1;
if (rActualLength != nil)
*rActualLength = vActualLength;
return noErr;
}
// Fall through to keep space characters
default:
*vReadData = *vBufferData;
vReadData++;
vActualLength++;
pMaxLength--;
if (pMaxLength == 0) {
pBuffer->BufferOffset += vIndex + 1;
pBuffer->BytesAvailable -= vIndex + 1;
if (rActualLength != nil)
*rActualLength = vActualLength;
return noErr;
}
break;
} // switch
vBufferData++;
} // for
pBuffer->BufferOffset = pBuffer->BytesInBuffer;
pBuffer->BytesAvailable = 0;
} while (vErr == noErr);
return vErr;
}
static OSErr ReadOneCharacter(tInputBuffer *pBuffer, char *rCharacter)
{
if (pBuffer->BytesAvailable == 0)
return eofErr;
*rCharacter = pBuffer->Buffer[pBuffer->BufferOffset];
pBuffer->BufferOffset++;
pBuffer->BytesAvailable--;
return noErr;
}
#ifdef _MSC_VER
char szBinHexFileHeader[40] = "(This file must be converted with BinHex";
#endif
static OSErr FindBinHexHeader(tInputBuffer *pBuffer)
{
Str255 vText;
OSErr vErr;
long vActualLength;
Boolean vFoundString1;
vFoundString1 = false;
do {
vErr = SkipReturn(pBuffer);
if (vErr != noErr) return vErr;
vErr = ReadFileToReturn(pBuffer, true, &vText[1], sizeof(vText) - 1, &vActualLength);
if (vErr != noErr) return vErr;
#ifdef _MSC_VER
// The MSVC runtimes don't include PLpos, so hopefully this is a
// reasonable check to see if the file begins with this magic string.
if (vActualLength >= sizeof(szBinHexFileHeader) &&
memcmp(szBinHexFileHeader, &vText[1], sizeof(szBinHexFileHeader)) == 0) {
#else
vText[0] = vActualLength;
if (PLpos("\p(This file must be converted with BinHex", vText) == 1) {
#endif
char vChar;
vErr = SkipReturn(pBuffer);
if (vErr != noErr) return vErr;
vErr = ReadOneCharacter(pBuffer, &vChar);
if (vErr != noErr) return vErr;
if (vChar == ':')
return noErr;
}
} while (vErr == noErr);
return vErr;
}
extern short gResFile;
static Boolean BinHexBuffer_New(tBinHexBuffer **rBuffer)
{
Handle vConversionTable;
short curResFile;
if (gResFile == -1) return false;
*rBuffer = (tBinHexBuffer *)NewPtrClear(sizeof(tBinHexBuffer));
if (MemError() != noErr) return false;
curResFile = CurResFile();
UseResFile(gResFile);
vConversionTable = Get1Resource('HQXT', 128);
BlockMove(*vConversionTable, (**rBuffer).ConversionTable, 256);
UseResFile(curResFile);
return true;
}
static void BinHexBuffer_Dispose(tBinHexBuffer *pBuffer)
{
DisposePtr((Ptr)pBuffer);
}
static OSErr BinHexBuffer_LoadFromFile(tBinHexBuffer *pBuffer, tInputBuffer *pInputBuffer, short pFileRef)
{
OSErr vErr;
UInt32 vBytesUsed;
// Load the source buffer
if (pInputBuffer->BytesAvailable == 0) {
vErr = InputBuffer_LoadFromFile(pInputBuffer, pFileRef);
if (vErr != noErr) return vErr;
}
if (!BinHexBuffer_Stuff(pBuffer, &pInputBuffer->Buffer[pInputBuffer->BufferOffset], pInputBuffer->BytesAvailable, &vBytesUsed))
return paramErr;
pInputBuffer->BufferOffset += vBytesUsed;
pInputBuffer->BytesAvailable -= vBytesUsed;
return noErr;
}
static Boolean BinHexBuffer_Stuff(tBinHexBuffer *pBuffer, UInt8 *pData, UInt32 pNumberOfBytes, UInt32 *rBytesStuffed)
{
UInt8 * vNextSourceByte;
int vHoldingPhase;
int vHoldingByte;
UInt8 * vNextDestByte;
int vPreviousByte;
Boolean vRepeatFlagFound;
UInt32 vBytesAvailable;
if (pBuffer->BytesInBuffer == pBuffer->BufferOffset) {
pBuffer->BytesInBuffer = 0;
pBuffer->BufferOffset = 0;
}
if (pBuffer->TerminatorFound)
return pNumberOfBytes;
// Restore our local variables
vNextSourceByte = pData;
vNextDestByte = &pBuffer->Buffer[pBuffer->BufferOffset];
vHoldingPhase = pBuffer->HoldingPhase;
vHoldingByte = pBuffer->HoldingByte;
vPreviousByte = pBuffer->PreviousByte;
vRepeatFlagFound = pBuffer->RepeatFlagFound;
vBytesAvailable = pNumberOfBytes;
while ((vBytesAvailable > 0)
&& (!pBuffer->TerminatorFound)
&& (pBuffer->BytesInBuffer < cBuffer_Size - 256)) {
int vByte6;
int vByte;
vByte6 = pBuffer->ConversionTable[*vNextSourceByte++];
vBytesAvailable--;
if (vByte6 < 64) {
switch (vHoldingPhase) {
case 0:
vHoldingByte = vByte6 << 2;
vHoldingPhase = 1;
continue;
case 1:
vByte = vHoldingByte | (vByte6 >> 4);
vHoldingByte = vByte6 << 4;
vHoldingPhase = 2;
break;
case 2:
vByte = vHoldingByte | (vByte6 >> 2);
vHoldingByte = vByte6 << 6;
vHoldingPhase = 3;
break;
case 3:
vByte = vHoldingByte | vByte6;
vHoldingPhase = 0;
break;
} // switch
vByte = vByte & 0x0ff;
if (vRepeatFlagFound) {
vRepeatFlagFound = false;
if (vByte == 0)
vByte = 0x90;
else {
while (--vByte) {
*vNextDestByte = vPreviousByte;
vNextDestByte++;
pBuffer->BytesInBuffer++;
} // for
continue;
}
}
else {
if (vByte == 0x90) {
vRepeatFlagFound = true;
continue;
}
}
*vNextDestByte++ = vByte;
pBuffer->BytesInBuffer++;
vPreviousByte = vByte;
}
else {
switch (vByte6) {
case 0xfd: // whitespace (just ignore)
break;
case 0xfe:
pBuffer->TerminatorFound = true;
break;
case 0xff:
return false;
} // switch
}
} // while
pBuffer->HoldingPhase = vHoldingPhase;
pBuffer->HoldingByte = vHoldingByte;
pBuffer->PreviousByte = vPreviousByte;
pBuffer->RepeatFlagFound = vRepeatFlagFound;
pBuffer->BytesAvailable = pBuffer->BytesInBuffer - pBuffer->BufferOffset;
*rBytesStuffed = pNumberOfBytes - vBytesAvailable;
return true;
}
static OSErr BinHexBuffer_ReadByte(tBinHexBuffer *pBuffer, UInt8 *rByte, UInt16 *rChecksum)
{
if (pBuffer->BytesAvailable == 0)
return eofErr;
*rByte = pBuffer->Buffer[pBuffer->BufferOffset];
pBuffer->BufferOffset++;
pBuffer->BytesAvailable--;
if (rChecksum != nil)
AddByteToBinHexChecksum(*rByte, rChecksum);
return noErr;
}
static OSErr BinHexBuffer_ReadBytes(tBinHexBuffer *pBuffer, UInt8 *pReadBuffer, UInt32 pNumberOfBytes, UInt16 *rChecksum)
{
if (pNumberOfBytes > pBuffer->BytesAvailable)
return eofErr;
if (pNumberOfBytes == 0)
return eofErr;
BlockMove(&pBuffer->Buffer[pBuffer->BufferOffset], pReadBuffer, pNumberOfBytes);
if (rChecksum != nil)
AddBytesToBinHexChecksum(pReadBuffer, pNumberOfBytes, rChecksum);
pBuffer->BufferOffset += pNumberOfBytes;
pBuffer->BytesAvailable -= pNumberOfBytes;
return noErr;
}
static void AddByteToBinHexChecksum(UInt8 pValue, UInt16 *rChecksum)
{
SInt32 vChecksum;
int vIndex;
SInt32 vSeedValue;
#if cIgnoreChecksums
return;
#endif
vChecksum = ((UInt32)*rChecksum << 16) | ((UInt32)pValue << 8);
vSeedValue = 0x10210000;
vIndex = 8;
do {
if (vChecksum < 0)
vChecksum = (vChecksum << 1) ^ vSeedValue;
else
vChecksum = vChecksum << 1;
} while (--vIndex > 0);
*rChecksum = vChecksum >> 16;
}
static void AddBytesToBinHexChecksum(UInt8 *pValues, UInt32 pNumberOfBytes, UInt16 *rChecksum)
{
#if cIgnoreChecksums
return;
#endif
while (pNumberOfBytes > 0) {
AddByteToBinHexChecksum(*pValues, rChecksum);
pValues++;
pNumberOfBytes--;
} // while
}
static OSErr GetBinHexFileHeader(tBinHexBuffer *pBuffer, Boolean *rHeaderIsValid, tBinHexFileHeader *rHeader)
{
UInt16 vCalculatedChecksum;
UInt8 vTempByte;
OSErr vErr;
UInt16 vActualChecksum;
vCalculatedChecksum = 0;
*rHeaderIsValid = false;
vErr = BinHexBuffer_ReadByte(pBuffer, &rHeader->FileName[0], &vCalculatedChecksum);
if (vErr != noErr) return vErr;
vErr = BinHexBuffer_ReadBytes(pBuffer, &rHeader->FileName[1], rHeader->FileName[0], &vCalculatedChecksum);
if (vErr != noErr) return vErr;
vErr = BinHexBuffer_ReadByte(pBuffer, &vTempByte, &vCalculatedChecksum);
if (vErr != noErr) return vErr;
if (vTempByte != 0) return vErr;
vErr = BinHexBuffer_ReadBytes(pBuffer, (UInt8 *)&rHeader->FileType, sizeof(tBinHexFileHeader) - sizeof(Str63), &vCalculatedChecksum);
if (vErr != noErr) return vErr;
vErr = BinHexBuffer_ReadBytes(pBuffer, (UInt8 *)&vActualChecksum, sizeof(short), nil);
if (vErr != noErr) return vErr;
AddByteToBinHexChecksum(0, &vCalculatedChecksum);
AddByteToBinHexChecksum(0, &vCalculatedChecksum);
#if cIgnoreChecksums
*rHeaderIsValid = true;
#else
*rHeaderIsValid = (vActualChecksum == vCalculatedChecksum);
#endif
return noErr;
}
static Boolean CopyBinHexToFile(tBinHexBuffer *pBuffer, tInputBuffer *pInputBuffer, short pSourceFileRef, short pDestFileRef, UInt32 pNumberOfBytes, UInt16 *rChecksum)
{
OSErr vErr;
while (pNumberOfBytes > 0) {
long vNumberOfBytes;
if (pBuffer->BytesAvailable == 0) {
vErr = BinHexBuffer_LoadFromFile(pBuffer, pInputBuffer, pSourceFileRef);
if (vErr != noErr) return false;;
} // if
if (pNumberOfBytes > pBuffer->BytesAvailable)
vNumberOfBytes = pBuffer->BytesAvailable;
else
vNumberOfBytes = pNumberOfBytes;
vErr = FSWrite(pDestFileRef, &vNumberOfBytes, &pBuffer->Buffer[pBuffer->BufferOffset]);
if (vErr != noErr) return false;;
if (rChecksum != nil)
AddBytesToBinHexChecksum(&pBuffer->Buffer[pBuffer->BufferOffset], vNumberOfBytes, rChecksum);
pNumberOfBytes -= vNumberOfBytes;
pBuffer->BufferOffset += vNumberOfBytes;
pBuffer->BytesAvailable -= vNumberOfBytes;
} // while
return true;
}
//----------------------------------------------
// InputBuffer
//----------------------------------------------
static Boolean InputBuffer_New(tInputBuffer **rBuffer)
{
*rBuffer = (tInputBuffer *)NewPtrClear(sizeof(tInputBuffer));
if (MemError() != noErr) return false;
return true;
}
static void InputBuffer_Dispose(tInputBuffer *pBuffer)
{
DisposePtr((Ptr)pBuffer);
}
static OSErr InputBuffer_LoadFromFile(tInputBuffer *pBuffer, short pFileRef)
{
OSErr vErr;
void * vBufferData;
long vMaximumBytes;
long vFileSize;
long vNumberOfBytes;
// Get the buffer pointer and maximum size
InputBuffer_GetPointer(pBuffer, &vBufferData, &vMaximumBytes);
// Calculate the number of bytes to read
GetEOF(pFileRef, &vFileSize);
vNumberOfBytes = vFileSize - pBuffer->FileOffset;
if (vNumberOfBytes == 0)
return eofErr;
if (vNumberOfBytes > vMaximumBytes)
vNumberOfBytes = vMaximumBytes;
// Read the data
vErr = SetFPos(pFileRef, fsFromStart, pBuffer->FileOffset);
if (vErr != noErr) return vErr;
vErr = FSRead(pFileRef, &vNumberOfBytes, vBufferData);
if (vErr != noErr) return vErr;
// Tell the buffer about the new data
InputBuffer_BytesAdded(pBuffer, vNumberOfBytes);
return noErr;
}
#ifdef MAC_IE_ONLY
static long InputBuffer_Stuff(tInputBuffer *pInputBuffer, void *pData, long pNumberOfBytes)
{
void * vBufferData;
long vNumberOfBytes;
InputBuffer_GetPointer(pInputBuffer, &vBufferData, &vNumberOfBytes);
if (vNumberOfBytes > pNumberOfBytes)
vNumberOfBytes = pNumberOfBytes;
BlockMove(pData, vBufferData, vNumberOfBytes);
InputBuffer_BytesAdded(pInputBuffer, vNumberOfBytes);
return vNumberOfBytes;
}
static void InputBuffer_DeleteOldData(tInputBuffer *pInputBuffer)
{
void * vSourceData;
long vNumberOfBytes;
vNumberOfBytes = pInputBuffer->BytesInBuffer - pInputBuffer->BufferOffset;
if (vNumberOfBytes == 0) {
pInputBuffer->BufferOffset = 0;
pInputBuffer->BytesInBuffer = 0;
return;
}
vSourceData = &pInputBuffer->Buffer[pInputBuffer->BufferOffset];
BlockMove(vSourceData, &pInputBuffer->Buffer[0], vNumberOfBytes);
pInputBuffer->BufferOffset = 0;
pInputBuffer->BytesInBuffer = vNumberOfBytes;
}
#endif
static void InputBuffer_GetPointer(tInputBuffer *pInputBuffer, void **rData, long *rMaxNumberOfBytes)
{
if (pInputBuffer->BufferOffset == pInputBuffer->BytesInBuffer) {
pInputBuffer->BufferOffset = 0;
pInputBuffer->BytesInBuffer = 0;
}
*rData = &pInputBuffer->Buffer[pInputBuffer->BytesInBuffer];
*rMaxNumberOfBytes = cBuffer_Size - pInputBuffer->BytesInBuffer;
}
static void InputBuffer_BytesAdded(tInputBuffer *pInputBuffer, long pNumberOfBytes)
{
pInputBuffer->BytesInBuffer += pNumberOfBytes;
pInputBuffer->BytesAvailable += pNumberOfBytes;
pInputBuffer->FileOffset += pNumberOfBytes;
}
#ifdef MAC_IE_ONLY
//----------------------------------------------
// BinHex Parser
//----------------------------------------------
Boolean BinHexParser_New(tBinHexClientData *pClientData, tBinHexParserCallbacks *pCallbacks, tBinHexParser **rParser)
{
tBinHexParser * vParser;
vParser = (tBinHexParser *)NewPtr(sizeof(struct tBinHexParser));
if (MemError() != noErr)
return false;
vParser->fClientData = pClientData;
vParser->fCallbacks = pCallbacks;
if (!InputBuffer_New(&vParser->fInputBuffer))
goto error1;
if (!BinHexBuffer_New(&vParser->fBinHexBuffer))
goto error2;
vParser->fParserState = cValidating_ParserState;
vParser->fByteCount = 0;
*rParser = vParser;
return true;
//-------------------
error2:
InputBuffer_Dispose(vParser->fInputBuffer);
error1:
DisposePtr((Ptr)vParser);
return false;
}
void BinHexParser_Dispose(tBinHexParser *pParser)
{
BinHexParser_Flush(pParser);
BinHexBuffer_Dispose(pParser->fBinHexBuffer);
InputBuffer_Dispose(pParser->fInputBuffer);
DisposePtr((Ptr)pParser);
}
Boolean BinHexParser_Flush(tBinHexParser *pParser)
{
if (pParser->fParserState == cValidating_ParserState) {
pParser->fParserState = cPassThrough_ParserState;
if (pParser->fInputBuffer->BytesInBuffer > 0)
if (!BinHexParser_WriteData(pParser, pParser->fInputBuffer->Buffer, pParser->fInputBuffer->BytesInBuffer))
return false;
} // if
return true;
}
void BinHexParser_Kill(tBinHexParser *pParser)
{
pParser->fParserState = cPassThrough_ParserState;
}
Boolean BinHexParser_WriteData(tBinHexParser *pParser, void *pData, long pDataLength)
{
while (pDataLength > 0) {
switch (pParser->fParserState) {
case cValidating_ParserState: {
UInt32 vBytesStuffed;
OSErr vErr;
// Reset the input buffer so that we're searching from the beginning each time (the search code isn't very bright)
pParser->fInputBuffer->BufferOffset = 0;
pParser->fInputBuffer->BytesAvailable = pParser->fInputBuffer->BytesInBuffer;
// Stuff as much of the new data into the buffer as will fit
vBytesStuffed = InputBuffer_Stuff(pParser->fInputBuffer, pData, pDataLength);
pData = (UInt8 *)pData + vBytesStuffed;
pDataLength -= vBytesStuffed;
// See if there's a header in there somewhere
vErr = FindBinHexHeader(pParser->fInputBuffer);
if (vErr == noErr) {
// Found the header - start parsing data
InputBuffer_DeleteOldData(pParser->fInputBuffer);
pParser->fParserState = cHeaderNameLength_ParserState;
pParser->fByteCount = 0;
// Call ourselves recursively to process the remains of the buffer
if ((pParser->fInputBuffer->BytesAvailable > 0)
&& !BinHexParser_WriteData(pParser, pParser->fInputBuffer->Buffer, pParser->fInputBuffer->BytesAvailable))
return false;
break;
} // if
if (pParser->fInputBuffer->BytesInBuffer == cBuffer_Size) {
// The buffer's full and we still haven't found the data - switch to pass-through mode
if (!BinHexParser_Flush(pParser))
return false;
} // if
break;
}
case cHeaderNameLength_ParserState:
case cHeaderName_ParserState:
case cHeaderNamePad_ParserState:
case cHeader_ParserState:
case cHeaderChecksum_ParserState:
case cDataFork_ParserState:
case cDataForkChecksum_ParserState:
case cResourceFork_ParserState:
case cResourceForkChecksum_ParserState: {
long vBytesStuffed;
if (!BinHexBuffer_Stuff(pParser->fBinHexBuffer, pData, pDataLength, &vBytesStuffed))
return false;
if ((pParser->fBinHexBuffer->BytesAvailable > 0)
&& !BinHexParser_WriteDecodedData(pParser, &pParser->fBinHexBuffer->Buffer[pParser->fBinHexBuffer->BufferOffset], pParser->fBinHexBuffer->BytesAvailable))
return false;
pParser->fBinHexBuffer->BufferOffset = pParser->fBinHexBuffer->BytesInBuffer;
pParser->fBinHexBuffer->BytesAvailable = 0;
pData = (UInt8 *)pData + vBytesStuffed;
pDataLength -= vBytesStuffed;
break;
}
case cPassThrough_ParserState:
if (!pParser->fCallbacks->WriteDF(pParser->fClientData, pData, pDataLength))
return false;
pDataLength = 0;
break;
case cDiscard_ParserState:
pDataLength = 0;
break;
default:
DebugStr("\pBinHexParser_WriteData: Illegal switch selector");
break;
} // switch
} // while
return true;
}
Boolean BinHexParser_IsBinHex(tBinHexParser *pParser)
{
return ((pParser->fParserState != cValidating_ParserState) && (pParser->fParserState != cPassThrough_ParserState));
}
static Boolean BinHexParser_WriteDecodedData(tBinHexParser *pParser, void *pData, long pDataLength)
{
while (pDataLength > 0) {
switch (pParser->fParserState) {
case cHeaderNameLength_ParserState:
pParser->fCalculatedChecksum = 0;
pParser->fByteCount = *(UInt8 *)pData;
AddByteToBinHexChecksum(pParser->fByteCount, &pParser->fCalculatedChecksum);
pData = (UInt8 *)pData + 1;
pDataLength -= 1;
pParser->fHeader.FileName[0] = 0; // The length byte will get incremented as the name is read in
pParser->fParserState = cHeaderName_ParserState;
break;
case cHeaderName_ParserState: {
long vNumberOfBytes;
vNumberOfBytes = pParser->fByteCount;
if (vNumberOfBytes > pDataLength)
vNumberOfBytes = pDataLength;
BlockMove(pData, &pParser->fHeader.FileName[pParser->fHeader.FileName[0] + 1], vNumberOfBytes);
AddBytesToBinHexChecksum(pData, vNumberOfBytes, &pParser->fCalculatedChecksum);
pParser->fHeader.FileName[0] += vNumberOfBytes;
pData = (UInt8 *)pData + vNumberOfBytes;
pDataLength -= vNumberOfBytes;
pParser->fByteCount -= vNumberOfBytes;
if (pParser->fByteCount == 0) {
pParser->fParserState = cHeaderNamePad_ParserState;
} // if
break;
}
case cHeaderNamePad_ParserState:
if (*(UInt8 *)pData != 0)
return false;
AddByteToBinHexChecksum(*(UInt8 *)pData, &pParser->fCalculatedChecksum);
pData = (UInt8 *)pData + 1;
pDataLength -= 1;
pParser->fParserState = cHeader_ParserState;
pParser->fByteCount = 0;
break;
case cHeader_ParserState: {
long vNumberOfBytes;
vNumberOfBytes = (sizeof(tBinHexFileHeader) - sizeof(Str63)) - pParser->fByteCount;
if (vNumberOfBytes > pDataLength)
vNumberOfBytes = pDataLength;
BlockMove(pData, (UInt8 *)&pParser->fHeader.FileType + pParser->fByteCount, vNumberOfBytes);
AddBytesToBinHexChecksum(pData, vNumberOfBytes, &pParser->fCalculatedChecksum);
pParser->fByteCount += vNumberOfBytes;
pData = (UInt8 *)pData + vNumberOfBytes;
pDataLength -= vNumberOfBytes;
if (pParser->fByteCount == (sizeof(tBinHexFileHeader) - sizeof(Str63))) {
pParser->fParserState = cHeaderChecksum_ParserState;
pParser->fByteCount = sizeof(UInt16);
}
break;
}
case cHeaderChecksum_ParserState:
case cDataForkChecksum_ParserState:
case cResourceForkChecksum_ParserState: {
tBinHexFileInfo vFileInfo;
// Collect the two bytes for the checksum
if (pDataLength > 1) {
if (pParser->fByteCount == 1) {
*((UInt8 *)&pParser->fActualChecksum + 1) = *(UInt8 *)pData;
pData = (UInt8 *)pData + 1;
pDataLength -= 1;
}
else {
pParser->fActualChecksum = *(UInt16 *)pData;
pData = (UInt8 *)pData + sizeof(UInt16);
pDataLength -= sizeof(UInt16);
}
}
else {
*((UInt8 *)&pParser->fActualChecksum) = *(UInt8 *)pData;
pData = (UInt8 *)pData + 1;
pDataLength -= 1;
pParser->fByteCount -= 1;
break;
}
// Include two zero bytes in the calculated checksum
AddByteToBinHexChecksum(0, &pParser->fCalculatedChecksum);
AddByteToBinHexChecksum(0, &pParser->fCalculatedChecksum);
// Completed the header - verify the checksum
if (pParser->fActualChecksum != pParser->fCalculatedChecksum)
return false;
switch (pParser->fParserState) {
case cHeaderChecksum_ParserState: {
// Send the header to the client
BlockMove(pParser->fHeader.FileName, vFileInfo.fName, sizeof(Str63));
vFileInfo.fFileType = pParser->fHeader.FileType;
vFileInfo.fFileCreator = pParser->fHeader.FileCreator;
vFileInfo.fFinderFlags = pParser->fHeader.FinderFlags;
if (!pParser->fCallbacks->SetFileInfo(pParser->fClientData, &vFileInfo))
return false;
// Start parsing the data fork
pParser->fParserState = cDataFork_ParserState;
pParser->fByteCount = pParser->fHeader.DataLength;
pParser->fCalculatedChecksum = 0;
break;
}
case cDataForkChecksum_ParserState:
pParser->fParserState = cResourceFork_ParserState;
pParser->fByteCount = pParser->fHeader.ResourceLength;
pParser->fCalculatedChecksum = 0;
break;
case cResourceForkChecksum_ParserState:
pParser->fParserState = cDiscard_ParserState;
pParser->fByteCount = 0;
break;
} // switch
break;
}
case cDataFork_ParserState:
if (pDataLength >= pParser->fByteCount) {
AddBytesToBinHexChecksum(pData, pParser->fByteCount, &pParser->fCalculatedChecksum);
if (!pParser->fCallbacks->WriteDF(pParser->fClientData, pData, pParser->fByteCount))
return false;
pData = (void *)((UInt8 *)pData + pParser->fByteCount);
pDataLength -= pParser->fByteCount;
pParser->fParserState = cDataForkChecksum_ParserState;
pParser->fByteCount = sizeof(UInt16);
}
else {
AddBytesToBinHexChecksum(pData, pDataLength, &pParser->fCalculatedChecksum);
if (!pParser->fCallbacks->WriteDF(pParser->fClientData, pData, pDataLength))
return false;
pParser->fByteCount -= pDataLength;
pDataLength = 0;
}
break;
case cResourceFork_ParserState:
if (pDataLength >= pParser->fByteCount) {
AddBytesToBinHexChecksum(pData, pParser->fByteCount, &pParser->fCalculatedChecksum);
if (!pParser->fCallbacks->WriteRF(pParser->fClientData, pData, pParser->fByteCount))
return false;
pData = (void *)((UInt8 *)pData + pParser->fByteCount);
pDataLength -= pParser->fByteCount;
pParser->fParserState = cResourceForkChecksum_ParserState;
pParser->fByteCount = sizeof(UInt16);
}
else {
AddBytesToBinHexChecksum(pData, pDataLength, &pParser->fCalculatedChecksum);
if (!pParser->fCallbacks->WriteRF(pParser->fClientData, pData, pDataLength))
return false;
pParser->fByteCount -= pDataLength;
pDataLength = 0;
}
break;
case cPassThrough_ParserState:
if (!pParser->fCallbacks->WriteDF(pParser->fClientData, pData, pDataLength))
return false;
pDataLength = 0;
break;
case cDiscard_ParserState:
pDataLength = 0;
break;
default:
DebugStr("\pBinHexParser_WriteData: Illegal switch selector");
break;
} // switch
} // while
return true;
}
#endif